package com.uuah.server.component;

import java.util.Collection;
import java.util.Iterator;
import java.util.concurrent.atomic.AtomicBoolean;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.uuah.api.config.object.IObjectFactory;
import com.uuah.config.i18n.CoreMessages;
import com.uuah.config.i18n.MessageFactory;
import com.uuah.exception.AbstractUuahException;
import com.uuah.exception.UuahException;
import com.uuah.exception.lifecycle.DisposeException;
import com.uuah.exception.lifecycle.InitialisationException;
import com.uuah.server.api.IUuahEvent;
import com.uuah.server.api.IUuahMessage;
import com.uuah.server.api.component.IComponent;
import com.uuah.server.api.component.ILifecycleAdapter;
import com.uuah.server.api.component.ILifecycleAdapterFactory;
import com.uuah.server.api.component.model.IEntryPointResolver;
import com.uuah.server.api.component.model.IEntryPointResolverSet;
import com.uuah.server.api.component.service.IService;
import com.uuah.server.api.endpoint.IUuahEndpoint;
import com.uuah.server.component.model.resolvers.EntryPointResolverSet;
import com.uuah.server.config.i18n.ServerMessages;
import com.uuah.server.exception.AbstractUuahServerException;
import com.uuah.server.exception.service.ServiceException;
import com.uuah.server.exception.transformer.TransformerException;
import com.uuah.server.message.UuahMessage;
import com.uuah.server.transformer.TransformerTemplate;
import com.uuah.server.transport.NullPayload;
import com.uuah.server.transport.RequestContextHelper;

public abstract class AbstractComponent implements IComponent {

	protected transient final Logger logger = LoggerFactory.getLogger(this
			.getClass());

	protected IService service;
	protected final AtomicBoolean started = new AtomicBoolean(false);
	protected final AtomicBoolean stopping = new AtomicBoolean(false);
	protected final AtomicBoolean initialised = new AtomicBoolean(false);
	protected final AtomicBoolean disposing = new AtomicBoolean(false);
	protected final AtomicBoolean disposed = new AtomicBoolean(false);

	protected IEntryPointResolverSet entryPointResolverSet;
	protected IObjectFactory objectFactory;
	protected ILifecycleAdapterFactory lifecycleAdapterFactory;

	public AbstractComponent() {

	}

	private IUuahMessage invokeInternal(IUuahEvent event)
			throws DisposeException, UuahException,
			AbstractUuahServerException, ServiceException {
		RequestContextHelper.unsafeSetEvent(event);

		if (logger.isTraceEnabled()) {
			logger.trace("Invoking " + this.getClass().getName()
					+ "component for service " + service.getName());
		}

		checkDisposed();
		if (!(event.getEndpoint() instanceof IUuahEndpoint)) {
			throw new IllegalStateException(
					"Unable to process request event, components only process incoming events.");
		}
		if (stopping.get() || !started.get()) {
			throw new UuahException(ServerMessages.componentIsStopped(service
					.getName()));
		}

		try {
			Object result = doInvoke(event);
			IUuahMessage resultMessage = createResultMessage(event, result);
			return resultMessage;
		} catch (AbstractUuahServerException me) {
			throw me;
		} catch (Exception e) {
			throw new ServiceException(ServerMessages.failedToInvoke(this
					.toString()), event.getMessage(), service, e);
		}
	}

	public IUuahMessage invoke(IUuahEvent event)
			throws AbstractUuahServerException {
		try {
			return invokeInternal(event);
		} catch (DisposeException e) {
			throw new AbstractUuahServerException(e);
		} catch (UuahException e) {
			throw new AbstractUuahServerException(e);
		}
	}

	protected IUuahMessage createResultMessage(IUuahEvent event, Object result)
			throws TransformerException {
		if (result instanceof IUuahMessage) {
			return (IUuahMessage) result;
		} else if (result instanceof VoidResult) {
			event.transformMessage();
			return event.getMessage();
		} else if (result != null) {
			event.getMessage().applyTransformer(
					new TransformerTemplate(
							new TransformerTemplate.OverwitePayloadCallback(
									result)));
			return event.getMessage();
		} else {
			return new UuahMessage(NullPayload.getInstance());
		}
	}

	public String toString() {
		return this.getClass().getName() + " component for: "
				+ service.toString();
	}

	public void release() {
		// nothing to do
	}

	public void setService(IService service) {
		this.service = service;
	}

	public IService getService() {
		return service;
	}

	public final void initialise() throws InitialisationException {
		if (!initialised.get()) {
			if (logger.isInfoEnabled()) {
				logger.info("Initialising: " + this);
			}
			if (service == null) {
				throw new InitialisationException(
						MessageFactory
								.createStaticMessage("Component has not been initialized properly, no service."),
						this);
			}
			doInitialise();
			initialised.set(true);
		}
	}

	public void dispose() {
		disposing.set(true);
		try {
			if (started.get()) {
				stop();
			}
		} catch (AbstractUuahException e) {
			logger.error(ServerMessages.failedToStop(toString()).getMessage());
		}
		try {
			doDispose();
		} catch (Exception e) {
			logger.warn(ServerMessages.failedToDispose(toString()).toString(),
					e);
		} finally {
			disposed.set(true);
			disposing.set(false);
			initialised.set(false);
		}
	}

	public void stop() throws AbstractUuahException {
		if (!disposed.get() && started.get() && !stopping.get()) {
			stopping.set(true);
			if (logger.isInfoEnabled()) {
				logger.info("Stopping: " + this);
			}
			doStop();
			started.set(false);
			stopping.set(false);
		}
	}

	public void start() throws AbstractUuahException {
		checkDisposed();
		if (!started.get()) {
			if (logger.isInfoEnabled()) {
				logger.info("Starting: " + this);
			}

			doStart();
			started.set(true);
		}
	}

	protected void doStop() throws AbstractUuahException {
		// Default implementation is no-op
	}

	protected void checkDisposed() throws DisposeException {
		if (disposed.get()) {
			throw new DisposeException(ServerMessages
					.createStaticMessage("Cannot use a disposed component"),
					this);
		}
	}

	public AbstractComponent(IObjectFactory objectFactory) {
		this(objectFactory, null);
	}

	public AbstractComponent(IObjectFactory objectFactory,
			IEntryPointResolverSet entryPointResolverSet) {
		super();
		this.objectFactory = objectFactory;
		this.entryPointResolverSet = entryPointResolverSet;
	}

	protected Object doInvoke(IUuahEvent event) throws Exception {
		return invokeComponentInstance(event);
	}

	protected Object invokeComponentInstance(IUuahEvent event) throws Exception {
		ILifecycleAdapter componentLifecycleAdapter = null;
		try {
			componentLifecycleAdapter = borrowComponentLifecycleAdaptor();
			return componentLifecycleAdapter.invoke(event);
		} finally {
			returnComponentLifecycleAdaptor(componentLifecycleAdapter);
		}
	}

	public Class getObjectType() {
		return objectFactory.getObjectClass();
	}

	protected ILifecycleAdapter createLifeCycleAdaptor()
			throws AbstractUuahException, Exception {
		ILifecycleAdapter lifecycleAdapter;
		if (lifecycleAdapterFactory != null) {
			lifecycleAdapter = lifecycleAdapterFactory.create(objectFactory
					.getInstance(), this, entryPointResolverSet);
		} else {
			lifecycleAdapter = service.getModel().getLifecycleAdapterFactory()
					.create(objectFactory.getInstance(), this,
							entryPointResolverSet);
		}
		lifecycleAdapter.initialise();
		return lifecycleAdapter;
	}

	protected abstract ILifecycleAdapter borrowComponentLifecycleAdaptor()
			throws Exception;

	protected abstract void returnComponentLifecycleAdaptor(
			ILifecycleAdapter lifecycleAdapter) throws Exception;

	protected void doInitialise() throws InitialisationException {
		if (objectFactory == null) {
			throw new InitialisationException(CoreMessages
					.objectIsNull("object factory"), this);
		}
		// Spring���õ��������ʼ����ֱ��ʹ�õ�����Ҫ���ó�ʼ��
		objectFactory.initialise();
	}

	// @Override
	protected void doStart() throws AbstractUuahException {
		if (entryPointResolverSet == null) {
			entryPointResolverSet = service.getModel()
					.getEntryPointResolverSet();
		}
	}

	// @Override
	protected void doDispose() {
	}

	public IEntryPointResolverSet getEntryPointResolverSet() {
		return entryPointResolverSet;
	}

	public void setEntryPointResolverSet(
			IEntryPointResolverSet entryPointResolverSet) {
		this.entryPointResolverSet = entryPointResolverSet;
	}

	public void setEntryPointResolvers(Collection entryPointResolvers) {
		if (null == entryPointResolverSet) {
			entryPointResolverSet = new EntryPointResolverSet();
		}
		for (Iterator resolvers = entryPointResolvers.iterator(); resolvers
				.hasNext();) {
			entryPointResolverSet
					.addEntryPointResolver((IEntryPointResolver) resolvers
							.next());
		}
	}

	public IObjectFactory getObjectFactory() {
		return objectFactory;
	}

	public void setObjectFactory(IObjectFactory objectFactory) {
		this.objectFactory = objectFactory;
	}

	public ILifecycleAdapterFactory getLifecycleAdapterFactory() {
		return lifecycleAdapterFactory;
	}

	public void setLifecycleAdapterFactory(
			ILifecycleAdapterFactory lifecycleAdapterFactory) {
		this.lifecycleAdapterFactory = lifecycleAdapterFactory;
	}
}
